#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "n2base.h"
#include "BLStringPool.h"

TN2StringPool::TN2StringPool()
{
    alloc_size = 0;
    curptr = 0;
    base = 0;
    pool = NULL;

    base = 1024;
    pool = new char[base];
    if (pool == NULL)
        throw EBaseLibExp_NoMemory;
    alloc_size = base-16;
}

TN2StringPool::TN2StringPool(size_t incbase)
{
    alloc_size = 0;
    curptr = 0;
    base = 0;
    pool = NULL;

    if (incbase > 1024)
        base = incbase;
    else
        base = 1024;
    pool = new char[base];
    if (pool == NULL)
        throw EBaseLibExp_NoMemory;
    alloc_size = base-16;
}

TN2StringPool::~TN2StringPool()
{
    ClearAndFreePool();
}

size_t TN2StringPool::AddStr(const char* str)
{
    if (str == NULL)
        return 0;
    return AddByteData(str, strlen(str)+1, 1);
}

size_t TN2StringPool::AddUtf16Str(const unsigned short* str)
{
    size_t len;
    if (str == NULL)
        return 0;
    len = 0;
    while (str[len])
        len++;
    return AddByteData((const char*)str, (len+1)*2, 2);
}

size_t TN2StringPool::AddUtf32Str(const unsigned int* str)
{
    size_t len;
    if (str == NULL)
        return 0;
    len = 0;
    while (str[len])
        len++;
    return AddByteData((const char*)str, (len+1)*4, 4);
}

const char * TN2StringPool::GetStr(size_t offset)
{
    return &pool[offset];
}

const unsigned short * TN2StringPool::GetUtf16Str(size_t offset)
{
    return (const unsigned short*) (&pool[offset]);
}

const unsigned int * TN2StringPool::GetUtf32Str(size_t offset)
{
    return (const unsigned int*) (&pool[offset]);
}

size_t TN2StringPool::AddByteData(const char* str, size_t length, int align)
{
    size_t add;
    size_t i;
    size_t offset_backup;
    if (pool == NULL)
        return 0;
    if (align > 1) {
        if (curptr % align) {
            add = align - (curptr % align);
            curptr += add;
        }
    }

    if (curptr + length >= alloc_size)
        IncPoolSize();

    offset_backup = curptr;
    for(i = 0; i < length; i++)
        pool[curptr+i] = str[i];
    curptr += length;
    return offset_backup;
}

void TN2StringPool::IncPoolSize()
{
    size_t i;
    char * new_pool;

    n2print("IncPoolSize\n");
    alloc_size += base;
    new_pool = new char[alloc_size+16];
    if (new_pool == NULL)
        throw EBaseLibExp_NoMemory;
    for (i = 0; i < curptr; i++)
        new_pool[i] = pool[i];
    delete[] pool;

    pool = new_pool;
    return;
}

size_t TN2StringPool::GetPoolSize()
{
    return alloc_size + 16;
}

size_t TN2StringPool::GetUsedSize()
{
    return curptr;
}

void TN2StringPool::ClearPool()
{
    curptr = 0;
}

void TN2StringPool::ClearAndFreePool()
{
    curptr = 0;
    delete[] pool;
    pool = NULL;
    alloc_size = 0;
}



//=================================================================

TN2DataPool::TN2DataPool(size_t minSize, size_t blockbase)
{
    memset(this, 0, sizeof(TN2DataPool));
    minBlockSize = minSize;
    block_base = blockbase;
}

TN2DataPool::~TN2DataPool()
{
    ClearAllData();
    if (blocks) {
        delete[] blocks;
        blocks = NULL;
    }
    curblockid = 0;
    block_alloc = 0;
}

void TN2DataPool::IncreaseBlockNumber()
{
    size_t i;
    if (blocks)
    {
        size_t newAlloc;
        TN2DataPoolBlock * oldblocks;
        newAlloc = block_alloc + block_base;
        oldblocks = blocks;
        blocks = new TN2DataPoolBlock[newAlloc];
        if (blocks == NULL)
            throw EBaseLibExp_NoMemory;
        for (i = 0; i < block_alloc; i++)
        {
            blocks[i].data = oldblocks[i].data;
            blocks[i].blocksize = oldblocks[i].blocksize;
            blocks[i].used = oldblocks[i].used;
            oldblocks[i].data = NULL;
            oldblocks[i].blocksize = 0;
            oldblocks[i].used = 0;
        }
        for (; i < newAlloc; i++)
        {
            blocks[i].data = NULL;
            blocks[i].blocksize = 0;
            blocks[i].used = 0;
        }
        block_alloc = newAlloc;
        delete[] oldblocks;
    }
    else
    {
        blocks = new TN2DataPoolBlock[block_base];
        if (blocks == NULL)
            throw EBaseLibExp_NoMemory;
        block_alloc = block_base;
        for (i = 0; i < block_alloc; i++) {
            blocks[i].data = NULL;
            blocks[i].blocksize = 0;
            blocks[i].used = 0;
        }
    }
}

void TN2DataPool::AddBinaryData(unsigned char * data, size_t size)
{
    size_t i;
    TN2DataPoolBlock * curblock;

    if (blocks == NULL)
        IncreaseBlockNumber();
    curblock = &blocks[curblockid];
    if (curblock->data == NULL)
    {
        curblock->data = new unsigned char[minBlockSize];
        if (curblock->data == NULL)
            throw EBaseLibExp_NoMemory;
        curblock->used = 0;
        curblock->blocksize = minBlockSize;
    }

    if (curblock->used + size >= curblock->blocksize)
    {
        size_t  part1_size, part2_size;
        part1_size = curblock->blocksize - curblock->used;
        part2_size = size - part1_size;
        for (i = 0; i < part1_size; i++)
            curblock->data[curblock->used + i] = data[i];
        curblock->used += part1_size;

        curblockid++;
        if (curblockid >= block_alloc)
            IncreaseBlockNumber();

        curblock = &blocks[curblockid];
        if (curblock->data == NULL)
        {
            size_t newAllocSize;
            if (minBlockSize > part2_size)
                newAllocSize = minBlockSize;
            else
                newAllocSize = part2_size;

            curblock->data = new unsigned char[newAllocSize];
            if (curblock->data == NULL)
                throw EBaseLibExp_NoMemory;
            curblock->used = 0;
            curblock->blocksize = newAllocSize;
        }
        for (i = 0; i < part2_size; i++)
            curblock->data[curblock->used + i] = data[part1_size+i];
        curblock->used += part2_size;
    }
    else
    {
        for (i = 0; i < size; i++)
            curblock->data[curblock->used + i] = data[i];
        curblock->used += size;
    }
    datasize += size;
}

n2ulong_t TN2DataPool::GetTotalDataSize()
{
    return datasize;
}

void TN2DataPool::ClearAllData()
{
    size_t i;
    if (blocks == NULL)
        return;
    for (i = 0; i < block_alloc; i++)
    {
        if (blocks[i].data) {
            delete[] blocks[i].data;
            blocks[i].data = NULL;
            blocks[i].used = 0;
            blocks[i].blocksize = 0;
        }
    }
    curblockid = 0;
    datasize = 0;
}






